package com.czl.module_base.widget.timeselector;

import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import com.blankj.utilcode.util.ScreenUtils;
import com.czl.module_base.R;
import com.czl.module_base.utils.DateUtil;
import com.czl.module_base.utils.TextUtil;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

/**
 * @author liuli
 * @date 2015/11/27
 */
public class TimeSelector {
    public interface ResultHandler {
        /**
         * 回调结果
         *
         * @param time
         */
        void handle(String time);
    }

    public enum SCROLLTYPE {
        /**
         * 年
         */
        YEAR(1),
        /**
         * 月
         */
        MONTH(2),
        /**
         * 日
         */
        DAY(4),
        /**
         * 时
         */
        HOUR(8),
        /**
         * 分
         */
        MINUTE(16);

        SCROLLTYPE(int value) {
            this.value = value;
        }

        public int value;
    }

    private int scrollUnits = SCROLLTYPE.YEAR.value + SCROLLTYPE.MONTH.value + SCROLLTYPE.DAY.value
            + SCROLLTYPE.HOUR.value + SCROLLTYPE.MINUTE.value;
    private final ResultHandler handler;
    private final Context context;
    public static final String FORMAT_STR1 = "yyyy-MM-dd HH:mm";
    public static final String FORMAT_STR2 = "yyyy-MM-dd";
    public static final String FORMAT_STR3 = "yyyy-MM";
    public static final String DEFAULT_START_DATE = "1990-01-01 00:00";
    public static final String DEFAULT_END_DATE = "2099-12-31 00:00";
    private String curFormat = "yyyy-MM-dd HH:mm";

    private Dialog seletorDialog;
    private PickerView yearPv;
    private PickerView monthPv;
    private PickerView dayPv;
    private PickerView hourPv;
    private PickerView minutePv;
    private TextView dayTv;
    private TextView hourTv;
    private TextView minuteTv;

    private final int MAXMINUTE = 59;
    private int maxHour = 23;
    private final int MINMINUTE = 0;
    private int minHour = 0;
    private final int MAXMONTH = 12;

    private ArrayList<String> year, month, day, hour, minute;
    private int startYear, startMonth, startDay, startHour, startMininute,
            endYear, endMonth, endDay, endHour, endMininute,
            curYear, curMonth, curDay, curHour, curMininute,
            minuteWorkStart, minuteWorkEnd, hourWorkStart, hourWorkEnd;
    private boolean spanYear, spanMon, spanDay, spanHour, spanMin;
    private final Calendar selectedCalender = Calendar.getInstance();
    private final long ANIMATORDELAY = 200L;
    private final long CHANGEDELAY = 90L;
    private String workStartStr;
    private String workEndStr;
    private final Calendar startCalendar;
    private final Calendar currentCalendar;
    private final Calendar endCalendar;
    private TextView tvCancle;
    private TextView tvSelect;

    /**
     * @param context
     * @param resultHandler 选取时间后回调
     * @param startDate     format："yyyy-MM-dd HH:mm"
     * @param endDate
     */
    public TimeSelector(Context context, ResultHandler resultHandler, String startDate, String endDate, String format, Date curDate) {
        this.context = context;
        this.handler = resultHandler;
        startCalendar = Calendar.getInstance();
        currentCalendar = Calendar.getInstance();
        endCalendar = Calendar.getInstance();
        curFormat = format;
        startCalendar.setTime(DateUtil.parse(startDate, curFormat));
        endCalendar.setTime(DateUtil.parse(endDate, curFormat));
        currentCalendar.setTime(curDate);
        initDialog();
        initView();
    }

    /**
     * @param context
     * @param startDate
     * @param endDate
     * @param workStartTime 工作日起始时间 如：朝九晚五  format："09:00"
     * @param workEndTime   工作日结束时间  format："17:00"
     */
    public TimeSelector(Context context, ResultHandler resultHandler, String startDate, String endDate, String format,
                        Date curDate, String workStartTime, String workEndTime) {
        this(context, resultHandler, startDate, endDate, format, curDate);
        this.workStartStr = workStartTime;
        this.workEndStr = workEndTime;
    }

    public void show() {
        if (startCalendar.getTime().getTime() >= endCalendar.getTime().getTime()) {
            Toast.makeText(context, "起始时间应小于结束时间", Toast.LENGTH_LONG).show();
            return;
        }
        if (!excuteWorkTime()) {
            return;
        }
        initParameter();
        initTimer();
        addListener();
        seletorDialog.show();
    }

    private void initDialog() {
        if (seletorDialog == null) {
            seletorDialog = new Dialog(context, R.style.time_dialog);
            seletorDialog.setCancelable(true);
            seletorDialog.setCanceledOnTouchOutside(true);
            seletorDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
            seletorDialog.setContentView(R.layout.dialog_selector);
            Window window = seletorDialog.getWindow();
            window.setGravity(Gravity.BOTTOM);
            WindowManager.LayoutParams lp = window.getAttributes();
            lp.width = ScreenUtils.getScreenWidth();
            window.setAttributes(lp);
        }
    }

    private void initView() {
        yearPv = seletorDialog.findViewById(R.id.year_pv);
        monthPv = seletorDialog.findViewById(R.id.month_pv);
        dayPv = seletorDialog.findViewById(R.id.day_pv);
        hourPv = seletorDialog.findViewById(R.id.hour_pv);
        minutePv = seletorDialog.findViewById(R.id.minute_pv);
        tvCancle = seletorDialog.findViewById(R.id.tv_cancle);
        tvSelect = seletorDialog.findViewById(R.id.tv_select);
        dayTv = seletorDialog.findViewById(R.id.text_day);
        hourTv = seletorDialog.findViewById(R.id.text_hour);
        minuteTv = seletorDialog.findViewById(R.id.text_min);

        tvCancle.setOnClickListener(view -> seletorDialog.dismiss());
        tvSelect.setOnClickListener(view -> {
            handler.handle(DateUtil.format(selectedCalender.getTime(), curFormat));
            seletorDialog.dismiss();
        });
    }

    private void initParameter() {
        startYear = startCalendar.get(Calendar.YEAR);
        startMonth = startCalendar.get(Calendar.MONTH) + 1;
        startDay = startCalendar.get(Calendar.DAY_OF_MONTH);
        startHour = startCalendar.get(Calendar.HOUR_OF_DAY);
        startMininute = startCalendar.get(Calendar.MINUTE);
        endYear = endCalendar.get(Calendar.YEAR);
        endMonth = endCalendar.get(Calendar.MONTH) + 1;
        endDay = endCalendar.get(Calendar.DAY_OF_MONTH);
        endHour = endCalendar.get(Calendar.HOUR_OF_DAY);
        endMininute = endCalendar.get(Calendar.MINUTE);
        curYear = currentCalendar.get(Calendar.YEAR);
        curMonth = currentCalendar.get(Calendar.MONTH) + 1;
        curDay = currentCalendar.get(Calendar.DAY_OF_MONTH);
        curHour = currentCalendar.get(Calendar.HOUR_OF_DAY);
        curMininute = currentCalendar.get(Calendar.MINUTE);
        spanYear = startYear != endYear;
        spanMon = (!spanYear) && (startMonth != endMonth);
        spanDay = (!spanMon) && (startDay != endDay);
        spanHour = (!spanDay) && (startHour != endHour);
        spanMin = (!spanHour) && (startMininute != endMininute);
        selectedCalender.setTime(currentCalendar.getTime());
    }

    private void initTimer() {
        initArrayList();
        if (spanYear) {
            for (int i = startYear; i <= endYear; i++) {
                year.add(String.valueOf(i));
            }
            for (int i = startMonth; i <= MAXMONTH; i++) {
                month.add(fomatTimeUnit(i));
            }
            for (int i = startDay; i <= startCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {
                day.add(fomatTimeUnit(i));
            }
            for (int i = startHour; i <= maxHour; i++) {
                hour.add(fomatTimeUnit(i));
            }
            for (int i = startMininute; i <= MAXMINUTE; i++) {
                minute.add(fomatTimeUnit(i));
            }
        } else if (spanMon) {
            year.add(String.valueOf(startYear));
            for (int i = startMonth; i <= endMonth; i++) {
                month.add(fomatTimeUnit(i));
            }
            for (int i = startDay; i <= startCalendar.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {
                day.add(fomatTimeUnit(i));
            }
            for (int i = startHour; i <= maxHour; i++) {
                hour.add(fomatTimeUnit(i));
            }
            for (int i = startMininute; i <= MAXMINUTE; i++) {
                minute.add(fomatTimeUnit(i));
            }
        } else if (spanDay) {
            year.add(String.valueOf(startYear));
            month.add(fomatTimeUnit(startMonth));
            for (int i = startDay; i <= endDay; i++) {
                day.add(fomatTimeUnit(i));
            }
            for (int i = startHour; i <= maxHour; i++) {
                hour.add(fomatTimeUnit(i));
            }
            for (int i = startMininute; i <= MAXMINUTE; i++) {
                minute.add(fomatTimeUnit(i));
            }
        } else if (spanHour) {
            year.add(String.valueOf(startYear));
            month.add(fomatTimeUnit(startMonth));
            day.add(fomatTimeUnit(startDay));
            for (int i = startHour; i <= endHour; i++) {
                hour.add(fomatTimeUnit(i));
            }
            for (int i = startMininute; i <= MAXMINUTE; i++) {
                minute.add(fomatTimeUnit(i));
            }
        } else if (spanMin) {
            year.add(String.valueOf(startYear));
            month.add(fomatTimeUnit(startMonth));
            day.add(fomatTimeUnit(startDay));
            hour.add(fomatTimeUnit(startHour));
            for (int i = startMininute; i <= endMininute; i++) {
                minute.add(fomatTimeUnit(i));
            }
        }
        loadComponent();
    }

    private boolean excuteWorkTime() {
        boolean res = true;
        if (!TextUtil.isEmpty(workStartStr) && !TextUtil.isEmpty(workEndStr)) {
            String[] start = workStartStr.split(":");
            String[] end = workEndStr.split(":");
            hourWorkStart = Integer.parseInt(start[0]);
            minuteWorkStart = Integer.parseInt(start[1]);
            hourWorkEnd = Integer.parseInt(end[0]);
            minuteWorkEnd = Integer.parseInt(end[1]);
            Calendar workStartCalendar = Calendar.getInstance();
            Calendar workEndCalendar = Calendar.getInstance();
            workStartCalendar.setTime(startCalendar.getTime());
            workEndCalendar.setTime(endCalendar.getTime());
            workStartCalendar.set(Calendar.HOUR_OF_DAY, hourWorkStart);
            workStartCalendar.set(Calendar.MINUTE, minuteWorkStart);
            workEndCalendar.set(Calendar.HOUR_OF_DAY, hourWorkEnd);
            workEndCalendar.set(Calendar.MINUTE, minuteWorkEnd);

            Calendar startTime = Calendar.getInstance();
            Calendar endTime = Calendar.getInstance();
            Calendar startWorkTime = Calendar.getInstance();
            Calendar endWorkTime = Calendar.getInstance();

            startTime.set(Calendar.HOUR_OF_DAY, startCalendar.get(Calendar.HOUR_OF_DAY));
            startTime.set(Calendar.MINUTE, startCalendar.get(Calendar.MINUTE));
            endTime.set(Calendar.HOUR_OF_DAY, endCalendar.get(Calendar.HOUR_OF_DAY));
            endTime.set(Calendar.MINUTE, endCalendar.get(Calendar.MINUTE));

            startWorkTime.set(Calendar.HOUR_OF_DAY, workStartCalendar.get(Calendar.HOUR_OF_DAY));
            startWorkTime.set(Calendar.MINUTE, workStartCalendar.get(Calendar.MINUTE));
            endWorkTime.set(Calendar.HOUR_OF_DAY, workEndCalendar.get(Calendar.HOUR_OF_DAY));
            endWorkTime.set(Calendar.MINUTE, workEndCalendar.get(Calendar.MINUTE));

            if (startTime.getTime().getTime() == endTime.getTime().getTime() ||
                    (startWorkTime.getTime().getTime() < startTime.getTime().getTime() &&
                            endWorkTime.getTime().getTime() < startTime.getTime().getTime())) {
                Toast.makeText(context, "时间参数错误", Toast.LENGTH_LONG).show();
                return false;
            }
            startCalendar.setTime(startCalendar.getTime().getTime() < workStartCalendar.getTime().getTime()
                    ? workStartCalendar.getTime() : startCalendar.getTime());
            endCalendar.setTime(endCalendar.getTime().getTime() > workEndCalendar.getTime().getTime()
                    ? workEndCalendar.getTime() : endCalendar.getTime());
            minHour = workStartCalendar.get(Calendar.HOUR_OF_DAY);
            maxHour = workEndCalendar.get(Calendar.HOUR_OF_DAY);
        }
        return res;
    }

    private String fomatTimeUnit(int unit) {
        return unit < 10 ? "0" + unit : String.valueOf(unit);
    }

    private void initArrayList() {
        if (year == null) {
            year = new ArrayList<>();
        }
        if (month == null) {
            month = new ArrayList<>();
        }
        if (day == null) {
            day = new ArrayList<>();
        }
        if (hour == null) {
            hour = new ArrayList<>();
        }
        if (minute == null) {
            minute = new ArrayList<>();
        }
        year.clear();
        month.clear();
        day.clear();
        hour.clear();
        minute.clear();
    }


    private void addListener() {
        yearPv.setOnSelectListener(text -> {
            selectedCalender.set(Calendar.YEAR, Integer.parseInt(text));
            monthChange();
        });
        monthPv.setOnSelectListener(text -> {
            selectedCalender.set(Calendar.MONTH, Integer.parseInt(text) - 1);
            dayChange();
        });
        dayPv.setOnSelectListener(text -> {
            selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(text));
            hourChange();
        });
        hourPv.setOnSelectListener(text -> {
            selectedCalender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(text));
            minuteChange();
        });
        minutePv.setOnSelectListener(text -> selectedCalender.set(Calendar.MINUTE, Integer.parseInt(text)));
    }

    private void loadComponent() {
        yearPv.setData(year);
        monthPv.setData(month);
        dayPv.setData(day);
        hourPv.setData(hour);
        minutePv.setData(minute);
        yearPv.setSelected(curYear + "");
        monthPv.setSelected(curMonth < 10 ? "0" + curMonth : curMonth + "");
        dayPv.setSelected(curDay < 10 ? "0" + curDay : curDay + "");
        hourPv.setSelected(curHour < 10 ? "0" + curHour : curHour + "");
        minutePv.setSelected(curMininute < 10 ? "0" + curMininute : curMininute + "");
        excuteScroll();
    }

    private void excuteScroll() {
        yearPv.setCanScroll(year.size() > 1 && (scrollUnits & SCROLLTYPE.YEAR.value) == SCROLLTYPE.YEAR.value);
        monthPv.setCanScroll(month.size() > 1 && (scrollUnits & SCROLLTYPE.MONTH.value) == SCROLLTYPE.MONTH.value);
        if (day.size() > 1 && (scrollUnits & SCROLLTYPE.DAY.value) == SCROLLTYPE.DAY.value) {
            dayPv.setCanScroll(true);
            dayPv.setVisibility(View.VISIBLE);
            dayTv.setVisibility(View.VISIBLE);
        } else {
            dayPv.setCanScroll(false);
            dayPv.setVisibility(View.GONE);
            dayTv.setVisibility(View.GONE);
        }
        if (hour.size() > 1 && (scrollUnits & SCROLLTYPE.HOUR.value) == SCROLLTYPE.HOUR.value) {
            hourPv.setCanScroll(true);
            hourPv.setVisibility(View.VISIBLE);
            hourTv.setVisibility(View.VISIBLE);
        } else {
            hourPv.setCanScroll(false);
            hourPv.setVisibility(View.GONE);
            hourTv.setVisibility(View.GONE);
        }
        if (minute.size() > 1 && (scrollUnits & SCROLLTYPE.MINUTE.value) == SCROLLTYPE.MINUTE.value) {
            minutePv.setCanScroll(true);
            minutePv.setVisibility(View.VISIBLE);
            minuteTv.setVisibility(View.VISIBLE);
        } else {
            minutePv.setCanScroll(false);
            minutePv.setVisibility(View.GONE);
            minuteTv.setVisibility(View.GONE);
        }
    }

    private void monthChange() {
        month.clear();
        int selectedYear = selectedCalender.get(Calendar.YEAR);
        if (selectedYear == startYear) {
            for (int i = startMonth; i <= MAXMONTH; i++) {
                month.add(fomatTimeUnit(i));
            }
        } else if (selectedYear == endYear) {
            for (int i = 1; i <= endMonth; i++) {
                month.add(fomatTimeUnit(i));
            }
        } else {
            for (int i = 1; i <= MAXMONTH; i++) {
                month.add(fomatTimeUnit(i));
            }
        }
        selectedCalender.set(Calendar.MONTH, Integer.parseInt(month.get(0)) - 1);
        monthPv.setData(month);
        monthPv.setSelected(0);
        excuteAnimator(ANIMATORDELAY, monthPv);
        monthPv.postDelayed(this::dayChange, CHANGEDELAY);
    }

    private void dayChange() {
        day.clear();
        int selectedYear = selectedCalender.get(Calendar.YEAR);
        int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1;
        if (selectedYear == startYear && selectedMonth == startMonth) {
            for (int i = startDay; i <= selectedCalender.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {
                day.add(fomatTimeUnit(i));
            }
        } else if (selectedYear == endYear && selectedMonth == endMonth) {
            for (int i = 1; i <= endDay; i++) {
                day.add(fomatTimeUnit(i));
            }
        } else {
            for (int i = 1; i <= selectedCalender.getActualMaximum(Calendar.DAY_OF_MONTH); i++) {
                day.add(fomatTimeUnit(i));
            }
        }
        selectedCalender.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day.get(0)));
        dayPv.setData(day);
        dayPv.setSelected(0);
        excuteAnimator(ANIMATORDELAY, dayPv);
        dayPv.postDelayed(this::hourChange, CHANGEDELAY);
    }

    private void hourChange() {
        hour.clear();
        int selectedYear = selectedCalender.get(Calendar.YEAR);
        int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1;
        int selectedDay = selectedCalender.get(Calendar.DAY_OF_MONTH);

        if (selectedYear == startYear && selectedMonth == startMonth && selectedDay == startDay) {
            for (int i = startHour; i <= maxHour; i++) {
                hour.add(fomatTimeUnit(i));
            }
        } else if (selectedYear == endYear && selectedMonth == endMonth && selectedDay == endDay) {
            for (int i = minHour; i <= endHour; i++) {
                hour.add(fomatTimeUnit(i));
            }
        } else {
            for (int i = minHour; i <= maxHour; i++) {
                hour.add(fomatTimeUnit(i));
            }
        }
        selectedCalender.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour.get(0)));
        hourPv.setData(hour);
        hourPv.setSelected(0);
        excuteAnimator(ANIMATORDELAY, hourPv);
        hourPv.postDelayed(this::minuteChange, CHANGEDELAY);
    }

    private void minuteChange() {
        minute.clear();
        int selectedYear = selectedCalender.get(Calendar.YEAR);
        int selectedMonth = selectedCalender.get(Calendar.MONTH) + 1;
        int selectedDay = selectedCalender.get(Calendar.DAY_OF_MONTH);
        int selectedHour = selectedCalender.get(Calendar.HOUR_OF_DAY);

        if (selectedYear == startYear && selectedMonth == startMonth && selectedDay == startDay && selectedHour == startHour) {
            for (int i = startMininute; i <= MAXMINUTE; i++) {
                minute.add(fomatTimeUnit(i));
            }
        } else if (selectedYear == endYear && selectedMonth == endMonth && selectedDay == endDay && selectedHour == endHour) {
            for (int i = MINMINUTE; i <= endMininute; i++) {
                minute.add(fomatTimeUnit(i));
            }
        } else if (selectedHour == hourWorkStart) {
            for (int i = minuteWorkStart; i <= MAXMINUTE; i++) {
                minute.add(fomatTimeUnit(i));
            }
        } else if (selectedHour == hourWorkEnd) {
            for (int i = MINMINUTE; i <= minuteWorkEnd; i++) {
                minute.add(fomatTimeUnit(i));
            }
        } else {
            for (int i = MINMINUTE; i <= MAXMINUTE; i++) {
                minute.add(fomatTimeUnit(i));
            }
        }
        selectedCalender.set(Calendar.MINUTE, Integer.parseInt(minute.get(0)));
        minutePv.setData(minute);
        minutePv.setSelected(0);
        excuteAnimator(ANIMATORDELAY, minutePv);
        excuteScroll();
    }

    private void excuteAnimator(long animatordelay, View view) {
        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,
                0f, 1f);
        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,
                1.3f, 1f);
        PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,
                1.3f, 1f);
        ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ).setDuration(animatordelay).start();
    }

    /**
     * 设置选取时间文本 默认"选择"
     */
    public void setNextBtTip(String str) {
        tvSelect.setText(str);
    }

    public int setScrollUnit(SCROLLTYPE... scrolltypes) {
        scrollUnits = 0;
        for (SCROLLTYPE scrolltype : scrolltypes) {
            scrollUnits ^= scrolltype.value;
        }
        return scrollUnits;
    }

}
